    .cpu    cortex-m3
    .fpu    softvfp
    .syntax unified
    .thumb
    .text
    
    .equ    ICSR, 0xE000ED04                /* 中断控制状态寄存器 */
    .equ    PENDSV_SET_BIT, 0x10000000      /* PendSV 异常触发值 */

    .equ    SHPR3, 0xE000ED20               /* system priority register (3) */
    .equ    PENDSV_PRI_LOWEST, 0xFFFF0000   /* PendSV and SysTick priority value (lowest) */
    .equ    SCB_VTOR, 0xE000ED08            /* Vector Table Offset Register */

    .global le_hw_interrupt_disable
    .type le_hw_interrupt_disable, %function
le_hw_interrupt_disable:
    MRS     R0, PRIMASK
    CPSID   I
    BX      LR

    .global le_hw_interrupt_enable
    .type le_hw_interrupt_enable, %function
le_hw_interrupt_enable:
    MSR     PRIMASK, R0
    BX      LR

    .global le_hw_context_switch
    .type le_hw_context_switch, %function
le_hw_context_switch:
    /* 加载变量 le_thread_switch_interrupt_flag 地址到R2，因为R0，R1被入参占用 */
    LDR     R2, =le_thread_switch_interrupt_flag
    /* 从R2的地址读取值到R3，也就是 le_thread_switch_interrupt_flag 的值 */
    LDR     R3, [R2]
    /* 比较R3与立即数1,相等时跳转至 _reswitch */
    CMP     R3, #1
    BEQ     _reswitch
    /* 将R3修改为1，并写入R2的地址中，也就是设置 le_thread_switch_interrupt_flag = 1 */
    MOV     R3, #1
    STR     R3, [R2]

    /* 将 le_sp_from_thread 的地址保存到R2中，并将第一个入参写入到R2的地址中，
       也就是设置 le_sp_from_thread = 第一个参数 */
    LDR     R2, =le_sp_from_thread
    STR     R0, [R2]

_reswitch:
    /* 将 le_sp_to_thread 的地址保存到R2中，并将第二个入参写入到R2的地址中，
       也就是设置 le_sp_to_thread = 第二个参数 */
    LDR     R2, =le_sp_to_thread
    STR     R1, [R2]

    /* 触发 PendSV 异常 */
    LDR     R0, =ICSR
    LDR     R1, =PENDSV_SET_BIT
    STR     R1, [R0]

    BX      LR

    .global PendSV_Handler
    .type PendSV_Handler, %function
PendSV_Handler:
    /* 保存中断状态，并关闭中断 */
    MRS     R2, PRIMASK
    CPSID   I

    /* 加载 le_thread_switch_interrupt_flag 的地址到R0，再从R0地址取值到R1
       如果R1是0的话就推出pendsv处理函数 */
    LDR     R0, =le_thread_switch_interrupt_flag
    LDR     R1, [R0]
    CBZ     R1, pendsv_exit

    /* 清除 le_thread_switch_interrupt_flag 的值 */
    MOV     R1, #0
    STR     R1, [R0]

    /* 检查 le_sp_from_thread 是否为0，为0的话说明是第一次进行任务切换，不需要保存from线程寄存器 */
    LDR     R0, =le_sp_from_thread
    LDR     R1, [R0]
    CBZ     R1, switch_to_thread

    /* 保存 form 线程的PSP值到R1 */
    MRS     R1, PSP
    /* 将R4 - R11的寄存器进行压栈，R1递减 */
    STMFD   R1!, {R4 - R11}
    /* 读取 le_sp_from_thread 值到R0 */
    LDR     R0, [R0]
    /* 将新的栈顶指针存入 le_sp_from_thread 值所指向的地址 */
    STR     R1, [R0]

switch_to_thread:
    /* 加载 le_sp_to_thread 的地址到 R1 */
    LDR     R1, =le_sp_to_thread
    /* 加载 le_sp_to_thread 的值到 R1 */
    LDR     R1, [R1]
    /* 加载 以le_sp_to_thread为地址的值 到 R1 */
    LDR     R1, [R1]
    /* R4 - R11 出栈 */
    LDMFD   R1!, {R4 - R11}
    /* 将新的栈顶指针存入 PSP 寄存器 */
    MSR     PSP, R1

pendsv_exit:
    /* 恢复刚进入PendSV时的中断状态 */
    MSR     PRIMASK, R2

    /* 使用PSP作为sp，返回；这里使用到了中断的返回值EXC_EXTERN来设置使用MSP还是PSP */
    ORR     LR, LR, #0x04
    BX      LR

    .global le_hw_context_switch_to
    .type le_hw_context_switch_to, %function
le_hw_context_switch_to:
    /* 将第一个入参存入变量 le_sp_to_thread */
    LDR     R1, =le_sp_to_thread
    STR     R0, [R1]

    /* 将 le_sp_from_thread 变量清零 */
    LDR     R1, =le_sp_from_thread
    MOV     R0, #0
    STR     R0, [R1]

    /* 设置线程切换标志位 le_thread_switch_interrupt_flag = 1 */
    LDR     R1, =le_thread_switch_interrupt_flag
    MOV     R0, #1
    STR     R0, [R1]

    /* 设置 PendSV and SysTick 异常优先级为最低 */
    LDR     R0, =SHPR3
    LDR     R1, =PENDSV_PRI_LOWEST
    LDR.W   R2, [R0,#0]             /* read */
    ORR     R1, R1, R2              /* modify */
    STR     R1, [R0]                /* write-back */

    /* 触发 PendSV 异常，进行上下文切换 */
    LDR     R0, =ICSR
    LDR     R1, =PENDSV_SET_BIT
    STR     R1, [R0]

    /* 读取中断向量表偏移寄存器地址到 R0 */
    LDR     r0, =SCB_VTOR
    /* 读取中断向量表偏移寄存器值到 R0 */
    LDR     r0, [r0]
    /* 从向量表偏移寄存器读取到SP指针的值存储到 R0 */
    LDR     r0, [r0]
    NOP
    /* MSP 寄存器重新赋值 */
    MSR     msp, r0

    /* 使能 中断 和 异常 */
    CPSIE   F
    CPSIE   I

    NOP
    /* 正常程序不会走到这里 */
    BX      LR
    
